import sys
import os
import logging
from datetime import datetime
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
    QListWidget, QTextEdit, QLineEdit, QPushButton, QLabel, QMessageBox, 
    QInputDialog, QTabWidget, QComboBox, QListWidgetItem, QFileDialog, 
    QDialog, QDialogButtonBox, QFormLayout, QSplitter
)
from PyQt5.QtCore import Qt, QSettings
from PyQt5.QtGui import QFont, QIcon
import markdown

# 导入自定义模块
from database_manager import DatabaseManager
from password_manager import PasswordManager
from crypto_utils import CryptoUtils
from config_manager import ConfigManager

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("secure_note_app.log"),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

class LoginDialog(QDialog):
    """登录对话框"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("登录安全笔记应用")
        self.setModal(True)
        self.setup_ui()
        
    def setup_ui(self):
        """设置UI"""
        layout = QFormLayout()
        
        self.username_input = QLineEdit()
        self.username_input.setPlaceholderText("请输入用户名")
        
        self.password_input = QLineEdit()
        self.password_input.setEchoMode(QLineEdit.Password)
        self.password_input.setPlaceholderText("请输入密码")
        
        self.register_button = QPushButton("注册新用户")
        self.register_button.clicked.connect(self.register_user)
        
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.validate_input)
        self.button_box.rejected.connect(self.reject)
        
        layout.addRow("用户名:", self.username_input)
        layout.addRow("密码:", self.password_input)
        layout.addRow(self.register_button)
        layout.addRow(self.button_box)
        
        self.setLayout(layout)
        
    def validate_input(self):
        """验证输入"""
        username = self.username_input.text().strip()
        password = self.password_input.text().strip()
        
        if not username:
            QMessageBox.warning(self, "输入错误", "用户名不能为空!")
            return
            
        if not password:
            QMessageBox.warning(self, "输入错误", "密码不能为空!")
            return
            
        self.accept()
        
    def register_user(self):
        """注册新用户"""
        username, ok = QInputDialog.getText(
            self, "注册", "请输入用户名:",
            QLineEdit.Normal, ""
        )
        
        if not ok or not username.strip():
            return
            
        username = username.strip()
        
        # 验证用户名
        if not self.is_valid_username(username):
            QMessageBox.warning(
                self, "无效用户名", 
                "用户名只能包含字母、数字和下划线，且长度在3-20字符之间!"
            )
            return
            
        password, ok = QInputDialog.getText(
            self, "注册", "请输入密码:", 
            QLineEdit.Password, ""
        )
        
        if not ok or not password:
            return
            
        # 验证密码强度
        if not self.is_strong_password(password):
            QMessageBox.warning(
                self, "弱密码", 
                "密码至少需要8个字符，包含大小写字母和数字!"
            )
            return
            
        confirm_password, ok = QInputDialog.getText(
            self, "注册", "请确认密码:", 
            QLineEdit.Password, ""
        )
        
        if not ok:
            return
            
        if password != confirm_password:
            QMessageBox.warning(self, "密码不匹配", "两次输入的密码不一致!")
            return
            
        # 创建用户
        if self.create_user(username, password):
            QMessageBox.information(self, "成功", "用户注册成功!")
            self.username_input.setText(username)
            self.password_input.setText(password)
        else:
            QMessageBox.warning(self, "错误", "用户注册失败，可能用户名已存在!")
            
    def is_valid_username(self, username: str) -> bool:
        """验证用户名是否有效"""
        if len(username) < 3 or len(username) > 20:
            return False
            
        # 只允许字母、数字和下划线
        return all(c.isalnum() or c == '_' for c in username)
        
    def is_strong_password(self, password: str) -> bool:
        """验证密码强度"""
        if len(password) < 8:
            return False
            
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        
        return has_upper and has_lower and has_digit
        
    def create_user(self, username: str, password: str) -> bool:
        """创建新用户"""
        db_path = f"notes_{username}.db"
        
        if os.path.exists(db_path):
            return False
            
        try:
            # 初始化数据库
            with DatabaseManager(db_path) as db:
                db.initialize_tables()
                db.insert_default_category()
                
                # 哈希密码
                hashed_password, salt = PasswordManager.hash_password(password)
                
                # 保存用户信息
                db.execute(
                    "INSERT INTO users (username, password_hash, salt) VALUES (?, ?, ?)",
                    (username, hashed_password, salt)
                )
                
            # 保存用户配置
            config_manager = ConfigManager()
            config_manager.set_user_config(username, {"db_path": db_path})
            
            logger.info(f"用户 {username} 创建成功")
            return True
            
        except Exception as e:
            logger.error(f"创建用户失败: {e}")
            # 清理失败创建的文件
            if os.path.exists(db_path):
                os.remove(db_path)
            return False


class SecureNoteApp(QMainWindow):
    """安全笔记应用主窗口"""
    
    def __init__(self, username: str, password: str):
        super().__init__()
        self.username = username
        self.password = password
        self.db_path = f"notes_{username}.db"
        self.current_note_id = None
        self.db_manager = None
        
        self.setup_ui()
        self.init_db()
        self.load_data()
        
    def setup_ui(self):
        """设置UI"""
        self.setWindowTitle(f'安全笔记应用 - {self.username}')
        self.setGeometry(100, 100, 1200, 800)
        
        # 中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QHBoxLayout(central_widget)
        
        # 左侧边栏
        sidebar = QWidget()
        sidebar.setFixedWidth(300)
        sidebar_layout = QVBoxLayout(sidebar)
        
        # 搜索
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("搜索笔记...")
        self.search_input.textChanged.connect(self.search_notes)
        sidebar_layout.addWidget(QLabel("搜索:"))
        sidebar_layout.addWidget(self.search_input)
        
        # 分类
        sidebar_layout.addWidget(QLabel("分类:"))
        self.category_combo = QComboBox()
        self.category_combo.currentTextChanged.connect(self.filter_by_category)
        sidebar_layout.addWidget(self.category_combo)
        
        # 标签
        sidebar_layout.addWidget(QLabel("标签:"))
        self.tags_list = QListWidget()
        self.tags_list.itemClicked.connect(self.filter_by_tag)
        sidebar_layout.addWidget(self.tags_list)
        
        # 笔记列表
        sidebar_layout.addWidget(QLabel("笔记列表:"))
        self.notes_list = QListWidget()
        self.notes_list.currentItemChanged.connect(self.show_note_content)
        sidebar_layout.addWidget(self.notes_list)
        
        # 右侧编辑区
        editor_widget = QWidget()
        editor_layout = QVBoxLayout(editor_widget)
        
        # 标题
        editor_layout.addWidget(QLabel("标题:"))
        self.title_input = QLineEdit()
        editor_layout.addWidget(self.title_input)
        
        # 分类选择
        editor_layout.addWidget(QLabel("分类:"))
        self.editor_category_combo = QComboBox()
        editor_layout.addWidget(self.editor_category_combo)
        
        # 标签编辑
        editor_layout.addWidget(QLabel("标签 (逗号分隔):"))
        self.tags_input = QLineEdit()
        editor_layout.addWidget(self.tags_input)
        
        # 选项卡 (编辑和预览)
        self.tabs = QTabWidget()
        self.editor = QTextEdit()
        self.preview = QTextEdit()
        self.preview.setReadOnly(True)
        self.tabs.addTab(self.editor, "编辑")
        self.tabs.addTab(self.preview, "预览")
        self.tabs.currentChanged.connect(self.update_preview)
        self.editor.textChanged.connect(self.update_preview)
        editor_layout.addWidget(self.tabs)
        
        # 按钮
        button_layout = QHBoxLayout()
        self.new_button = QPushButton("新建")
        self.new_button.clicked.connect(self.new_note)
        self.save_button = QPushButton("保存")
        self.save_button.clicked.connect(self.save_note)
        self.delete_button = QPushButton("删除")
        self.delete_button.clicked.connect(self.delete_note)
        self.export_button = QPushButton("导出")
        self.export_button.clicked.connect(self.export_note)
        self.import_button = QPushButton("导入")
        self.import_button.clicked.connect(self.import_note)
        
        button_layout.addWidget(self.new_button)
        button_layout.addWidget(self.save_button)
        button_layout.addWidget(self.delete_button)
        button_layout.addWidget(self.export_button)
        button_layout.addWidget(self.import_button)
        editor_layout.addLayout(button_layout)
        
        # 主布局
        main_layout.addWidget(sidebar)
        main_layout.addWidget(editor_widget)
        
    def init_db(self):
        """初始化数据库"""
        try:
            self.db_manager = DatabaseManager(self.db_path)
            self.db_manager.connect()
        except Exception as e:
            QMessageBox.critical(self, "数据库错误", f"无法连接数据库: {str(e)}")
            logger.error(f"数据库连接失败: {e}")
            sys.exit(1)
            
    def load_data(self):
        """加载数据"""
        self.load_categories()
        self.load_tags()
        self.load_notes()
        
    def load_categories(self):
        """加载分类"""
        self.category_combo.clear()
        self.editor_category_combo.clear()
        
        categories = self.db_manager.fetch_all(
            "SELECT id, name FROM categories ORDER BY name"
        )
        
        self.category_combo.addItem("所有分类", -1)
        self.editor_category_combo.addItem("未分类", None)
        
        for cat_id, name in categories:
            self.category_combo.addItem(name, cat_id)
            self.editor_category_combo.addItem(name, cat_id)
            
    def load_tags(self):
        """加载标签"""
        self.tags_list.clear()
        
        tags = self.db_manager.fetch_all(
            "SELECT id, name FROM tags ORDER BY name"
        )
        
        for tag_id, name in tags:
            item = QListWidgetItem(name)
            item.setData(Qt.UserRole, tag_id)
            self.tags_list.addItem(item)
            
    def load_notes(self, filter_category=None, filter_tag=None):
        """加载笔记"""
        self.notes_list.clear()
        
        query = """
            SELECT n.id, n.title, n.created_at 
            FROM notes n
            WHERE 1=1
        """
        params = []
        
        if filter_category and filter_category != -1:
            query += " AND n.category_id = ?"
            params.append(filter_category)
            
        if filter_tag:
            query += """
                AND n.id IN (
                    SELECT note_id FROM note_tags WHERE tag_id = ?
                )
            """
            params.append(filter_tag)
            
        query += " ORDER BY n.updated_at DESC"
        
        notes = self.db_manager.fetch_all(query, params)
        
        for note_id, title, created_at in notes:
            item = QListWidgetItem(f"{title} ({created_at[:10]})")
            item.setData(Qt.UserRole, note_id)
            self.notes_list.addItem(item)
            
    def show_note_content(self, current, previous):
        """显示笔记内容"""
        if current is None:
            return
            
        note_id = current.data(Qt.UserRole)
        
        query = """
            SELECT n.title, n.content, c.id, c.name, GROUP_CONCAT(t.name, ', ')
            FROM notes n
            LEFT JOIN categories c ON n.category_id = c.id
            LEFT JOIN note_tags nt ON n.id = nt.note_id
            LEFT JOIN tags t ON nt.tag_id = t.id
            WHERE n.id = ?
            GROUP BY n.id
        """
        
        result = self.db_manager.fetch_one(query, (note_id,))
        
        if result:
            title, content, cat_id, cat_name, tags = result
            decrypted_content = CryptoUtils.decrypt_data(content, self.password)
            
            if decrypted_content is None:
                QMessageBox.warning(self, "错误", "解密失败! 密码可能不正确。")
                return
                
            self.title_input.setText(title)
            self.editor.setPlainText(decrypted_content)
            
            if tags:
                self.tags_input.setText(tags)
            else:
                self.tags_input.clear()
                
            # 设置分类
            if cat_id:
                index = self.editor_category_combo.findData(cat_id)
                if index >= 0:
                    self.editor_category_combo.setCurrentIndex(index)
                    
            self.current_note_id = note_id
            
    def new_note(self):
        """新建笔记"""
        self.title_input.clear()
        self.editor.clear()
        self.tags_input.clear()
        self.editor_category_combo.setCurrentIndex(0)
        self.current_note_id = None
        self.notes_list.clearSelection()
        
    def save_note(self):
        """保存笔记"""
        title = self.title_input.text().strip()
        content = self.editor.toPlainText().strip()
        tags_text = self.tags_input.text().strip()
        category_index = self.editor_category_combo.currentIndex()
        category_id = self.editor_category_combo.itemData(category_index)
        
        if not title:
            QMessageBox.warning(self, "警告", "笔记标题不能为空!")
            return
            
        # 加密内容
        encrypted_content = CryptoUtils.encrypt_data(content, self.password)
        
        if encrypted_content is None:
            QMessageBox.warning(self, "错误", "加密失败!")
            return
            
        try:
            if self.current_note_id:
                # 更新现有笔记
                self.db_manager.execute(
                    """UPDATE notes SET title = ?, content = ?, category_id = ?, 
                    updated_at = ? WHERE id = ?""",
                    (title, encrypted_content, category_id, datetime.now(), self.current_note_id)
                )
                note_id = self.current_note_id
            else:
                # 创建新笔记
                cursor = self.db_manager.execute(
                    """INSERT INTO notes (title, content, category_id, created_at, updated_at) 
                    VALUES (?, ?, ?, ?, ?)""",
                    (title, encrypted_content, category_id, datetime.now(), datetime.now())
                )
                note_id = cursor.lastrowid if cursor else None
                
            if not note_id:
                raise Exception("无法获取笔记ID")
                
            # 处理标签
            self.update_note_tags(note_id, tags_text)
            
            self.load_data()
            
            if not self.current_note_id:
                # 如果是新笔记，选中最新的一条
                self.notes_list.setCurrentRow(0)
                
            QMessageBox.information(self, "成功", "笔记已保存!")
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存笔记失败: {str(e)}")
            logger.error(f"保存笔记失败: {e}")
            
    def update_note_tags(self, note_id: int, tags_text: str):
        """更新笔记标签"""
        # 清除现有标签关联
        self.db_manager.execute(
            "DELETE FROM note_tags WHERE note_id = ?", 
            (note_id,)
        )
        
        if tags_text:
            tags = [tag.strip() for tag in tags_text.split(',') if tag.strip()]
            
            for tag_name in tags:
                # 查找或创建标签
                tag = self.db_manager.fetch_one(
                    "SELECT id FROM tags WHERE name = ?", 
                    (tag_name,)
                )
                
                if tag:
                    tag_id = tag[0]
                else:
                    cursor = self.db_manager.execute(
                        "INSERT INTO tags (name) VALUES (?)", 
                        (tag_name,)
                    )
                    tag_id = cursor.lastrowid if cursor else None
                    
                if tag_id:
                    # 关联标签和笔记
                    self.db_manager.execute(
                        "INSERT OR IGNORE INTO note_tags (note_id, tag_id) VALUES (?, ?)", 
                        (note_id, tag_id)
                    )
                    
    def delete_note(self):
        """删除笔记"""
        if not self.current_note_id:
            QMessageBox.warning(self, "警告", "请先选择一个笔记!")
            return
            
        reply = QMessageBox.question(
            self, "确认删除", 
            "确定要删除这个笔记吗?",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            try:
                # 删除笔记（数据库设置了级联删除，会自动删除关联的标签）
                self.db_manager.execute(
                    "DELETE FROM notes WHERE id = ?", 
                    (self.current_note_id,)
                )
                self.new_note()
                self.load_data()
                QMessageBox.information(self, "成功", "笔记已删除!")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"删除笔记失败: {str(e)}")
                logger.error(f"删除笔记失败: {e}")
                
    def search_notes(self):
        """搜索笔记"""
        search_text = self.search_input.text().strip()
        
        if not search_text:
            self.load_notes()
            return
            
        self.notes_list.clear()
        
        query = """
            SELECT id, title, created_at FROM notes 
            WHERE title LIKE ? OR content LIKE ?
            ORDER BY updated_at DESC
        """
        
        notes = self.db_manager.fetch_all(
            query, 
            (f"%{search_text}%", f"%{search_text}%")
        )
        
        for note_id, title, created_at in notes:
            item = QListWidgetItem(f"{title} ({created_at[:10]})")
            item.setData(Qt.UserRole, note_id)
            self.notes_list.addItem(item)
            
    def filter_by_category(self, category_name):
        """按分类过滤笔记"""
        category_index = self.category_combo.currentIndex()
        category_id = self.category_combo.itemData(category_index)
        
        if category_id == -1:  # 所有分类
            self.load_notes()
        else:
            self.load_notes(filter_category=category_id)
            
    def filter_by_tag(self, item):
        """按标签过滤笔记"""
        tag_id = item.data(Qt.UserRole)
        self.load_notes(filter_tag=tag_id)
        
    def update_preview(self):
        """更新预览"""
        if self.tabs.currentIndex() == 1:  # 预览选项卡
            markdown_text = self.editor.toPlainText()
            html = markdown.markdown(markdown_text)
            self.preview.setHtml(html)
            
    def export_note(self):
        """导出笔记"""
        if not self.current_note_id:
            QMessageBox.warning(self, "警告", "请先选择一个笔记!")
            return
            
        file_path, _ = QFileDialog.getSaveFileName(
            self, "导出笔记", f"{self.title_input.text()}.json", "JSON Files (*.json)"
        )
        
        if file_path:
            try:
                query = """
                    SELECT n.title, n.content, c.name, GROUP_CONCAT(t.name, ', ')
                    FROM notes n
                    LEFT JOIN categories c ON n.category_id = c.id
                    LEFT JOIN note_tags nt ON n.id = nt.note_id
                    LEFT JOIN tags t ON nt.tag_id = t.id
                    WHERE n.id = ?
                    GROUP BY n.id
                """
                
                result = self.db_manager.fetch_one(query, (self.current_note_id,))
                
                if result:
                    title, content, category, tags = result
                    decrypted_content = CryptoUtils.decrypt_data(content, self.password)
                    
                    if decrypted_content is None:
                        QMessageBox.warning(self, "错误", "解密失败!")
                        return
                        
                    note_data = {
                        "title": title,
                        "content": decrypted_content,
                        "category": category,
                        "tags": tags.split(", ") if tags else [],
                        "exported_at": datetime.now().isoformat(),
                        "version": "1.0"
                    }
                    
                    with open(file_path, 'w', encoding='utf-8') as f:
                        import json
                        json.dump(note_data, f, ensure_ascii=False, indent=2)
                        
                    QMessageBox.information(self, "成功", "笔记导出成功!")
                    
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导出笔记失败: {str(e)}")
                logger.error(f"导出笔记失败: {e}")
                
    def import_note(self):
        """导入笔记"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "导入笔记", "", "JSON Files (*.json)"
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    import json
                    note_data = json.load(f)
                    
                self.title_input.setText(note_data.get('title', ''))
                self.editor.setPlainText(note_data.get('content', ''))
                
                tags = note_data.get('tags', [])
                if tags:
                    self.tags_input.setText(', '.join(tags))
                    
                category = note_data.get('category')
                if category:
                    index = self.editor_category_combo.findText(category)
                    if index >= 0:
                        self.editor_category_combo.setCurrentIndex(index)
                        
                self.current_note_id = None
                QMessageBox.information(self, "成功", "笔记导入成功!")
                
            except Exception as e:
                QMessageBox.warning(self, "错误", f"导入失败: {str(e)}")
                logger.error(f"导入笔记失败: {e}")
                
    def closeEvent(self, event):
        """关闭事件"""
        if self.db_manager:
            self.db_manager.close()
        event.accept()


def main():
    """主函数"""
    app = QApplication(sys.argv)
    
    # 设置应用样式
    app.setStyle('Fusion')
    
    # 尝试登录
    login_dialog = LoginDialog()
    if login_dialog.exec_() == QDialog.Accepted:
        username = login_dialog.username_input.text().strip()
        password = login_dialog.password_input.text().strip()
        
        # 验证用户
        db_path = f"notes_{username}.db"
        if not os.path.exists(db_path):
            QMessageBox.warning(None, "错误", "用户不存在!")
            sys.exit(1)
            
        try:
            # 验证密码
            with DatabaseManager(db_path) as db:
                result = db.fetch_one(
                    "SELECT password_hash FROM users WHERE username = ?", 
                    (username,)
                )
                
                if not result:
                    QMessageBox.warning(None, "错误", "用户不存在!")
                    sys.exit(1)
                    
                stored_password = result[0]
                
                if not PasswordManager.verify_password(password, stored_password):
                    QMessageBox.warning(None, "错误", "密码不正确!")
                    sys.exit(1)
                    
            # 启动应用
            window = SecureNoteApp(username, password)
            window.show()
            sys.exit(app.exec_())
            
        except Exception as e:
            QMessageBox.critical(None, "错误", f"应用启动失败: {str(e)}")
            logger.error(f"应用启动失败: {e}")
            sys.exit(1)
    else:
        sys.exit(0)


if __name__ == '__main__':
    main()